package org.openlaszlo.eu.io;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;

import org.apache.commons.io.FileUtils;
import org.openlaszlo.eu.io.dto.FileDescriptorDTO;

/**
 * 
 * This class provides a number of method to manage files. The result of each
 * method is a DTO object which can be serialized as an XML via the method
 * toXML().
 * 
 * @author Dominique Vernier
 */
public class FileManager {

	/**
	 * This method will walk through the File. If the file is a directory then
	 * the directory will be visited recursively. The FileDescriptorDTO.type is
	 * null if it is a directory otherwise it will contains the file extension.
	 * The FileDescriptorDTO.size is in byte and the total size of VISITED files
	 * for a directory.
	 * 
	 * @param file
	 *            The start file.
	 * @param level
	 *            The level of recursiveness n where n is the depth of the
	 *            resulted tree. (-1 means go deep as much as possible).
	 * @return A FileDescritorDTO of the visited file.
	 * @throws IOException
	 * @throws FileManagerException
	 */
	public FileDescriptorDTO browse(File file, int level)
			throws FileManagerException {

		// Create a new descriptor
		FileDescriptorDTO fileDescriptorDTO = new FileDescriptorDTO();
		if (!file.exists())
			throw new FileManagerException("File " + file.getPath()
					+ " doesn't exist");
		// Get the file name.
		String name = file.getName();
		fileDescriptorDTO.setName(name);
		fileDescriptorDTO.setPath(file.getPath());
		long size = 0;
		// If it is a directory then visit it.
		if (file.isDirectory() && (level > 0 || level == -1)) {
			// Loop on a file in the current directory.
			for (File f : file.listFiles()) {
				if (fileDescriptorDTO.getList() == null)
					fileDescriptorDTO
							.setList(new ArrayList<FileDescriptorDTO>());
				// Recursively call the method.
				if (level > 0)
					level--;
				FileDescriptorDTO fd = browse(f, level);
				// add the size
				size += fd.getSize();
				fileDescriptorDTO.getList().add(fd);
			}
		}
		// If it isn't a directory
		else {
			// add the size
			size += file.length();
			// Search the file extension and set the type.
			String[] ss = name.split("\\.");
			String type = null;
			if (ss.length > 1)
				type = ss[ss.length - 1];
			fileDescriptorDTO.setType(type);
		}
		// Set the size
		fileDescriptorDTO.setSize(size);
		return fileDescriptorDTO;
	}

	/**
	 * Idem as {@link} but of a directory name instead of a File.
	 * 
	 * @param name:
	 *            The directory name to visit.
	 * @param level:
	 *            The level of recursiveness n where n is the depth of the
	 *            resulted tree. (-1 means go deep as much as possible).
	 * @return A FileDescriptorDTO of the visited file.
	 * @throws IOException
	 * @throws FileManagerException
	 */
	public FileDescriptorDTO browse(String name, int level)
			throws FileManagerException {
		return browse(new File(name), level);
	}

	/**
	 * Idem as {@link} but of a directory name instead of a File and a level
	 * depth of -1.
	 * 
	 * @param name:
	 *            The directory name to visit.
	 * @return A FileDescritorDTO of the visited file.
	 * @throws IOException
	 * @throws FileManagerException
	 */
	public FileDescriptorDTO browse(String name) throws FileManagerException {
		return browse(name, -1);
	}

	/**
	 * 
	 * This file will return a byte array contains the content of the given file
	 * name.
	 * 
	 * @param file
	 *            The file to read.
	 * @return An byte array containing the content of the given file.
	 * @throws FileManagerException
	 *             if an IOException occurs while reading the file.
	 */
	public byte[] read(String fileName) throws FileManagerException {
		FileInputStream inFile = null;
		BufferedInputStream bis = null;
		byte[] array = null;
		try {
			inFile = new FileInputStream(fileName);
			bis = new BufferedInputStream(inFile);
			array = new byte[inFile.available()];
			bis.read(array);
		} catch (IOException ex) {
			throw new FileManagerException(ex);
		} finally {
			try {
				if (bis != null)
					bis.close();
				if (inFile != null)
					inFile.close();
			} catch (IOException ioe) {
				throw new FileManagerException(ioe);
			}
		}
		return array;
	}

	/**
	 * 
	 * This method will create a file with a given fileName and with the data as
	 * content. If the parent directory of the file doesn't exist it will be
	 * created. If the file already exists it will be replaced.
	 * 
	 * @param fileName
	 *            The name of the file.
	 * @param data
	 *            The content of the file.
	 * @throws FileManagerException
	 *             If an error occurs while saving the file.
	 */
	public void update(String fileName, byte[] data)
			throws FileManagerException {
		File file = new File(fileName);
		try {
			FileUtils.writeByteArrayToFile(file, data);
		} catch (IOException ioe) {
			throw new FileManagerException(ioe);
		}
	}

	/**
	 * 
	 * This methods delete a file or directory and all sub-files or directories.
	 * 
	 * @param fileName
	 *            The file or directory name.
	 * @throws FileManagerException
	 */
	public void delete(String fileName) throws FileManagerException {
		File file = new File(fileName);
		try {
			FileUtils.forceDelete(file);
		} catch (IOException ioe) {
			throw new FileManagerException(ioe);
		}
	}

	/**
	 * 
	 * This methods create a directory of the given fileName. If a parent
	 * directory doesn't exist it will be created.
	 * 
	 * @param directoryName
	 *            The directory name with it path
	 * @throws FileManagerException
	 */
	public void mkdir(String directoryName) throws FileManagerException {
		File file = new File(directoryName);
		try {
			FileUtils.forceMkdir(file);
		} catch (IOException ioe) {
			throw new FileManagerException(ioe);
		}
	}

	/**
	 * 
	 * Moves a source file to a destination
	 * @param srcFileName The source file name
	 * @param dstFileName The destination file name
	 * @throws FileManagerException
	 */
	public void move(String srcFileName, String dstFileName)
			throws FileManagerException {
		File srcFile = new File(srcFileName);
		File dstFile = new File(dstFileName);
		boolean success = srcFile.renameTo(dstFile);
		if (!success)
			throw new FileManagerException("Move of " + srcFileName + " to "
					+ dstFileName + " failed");
	}
}
